/**
 * HttpClientUtil.java
 * com.linyun.airline.common.util
 * Copyright (c) 2016, 北京科技有限公司版权所有.
*/

package com.sftz.framework.util;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.nutz.lang.Mirror;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.uxuexi.core.common.util.CollectionUtil;
import com.uxuexi.core.common.util.Util;

/**
 * http请求工具类
 * <p>
 *
 * @author   朱晓川
 */
public class HttpClientUtil {

	//打log用
	private static Log log = Logs.getLog(HttpClientUtil.class);

	private static PoolingHttpClientConnectionManager httpConnectionManager;

	private static RequestConfig requestConfig;

	private static final int MAX_TIMEOUT = 10000;

	/**
	 * HTTP请求成功状态码
	 */
	public static final int SUCCESS_CODE = 200;

	/**整个连接池最大连接数*/
	public static final int MAX_TOTAL = 100;
	/**每路由最大连接数，默认值是2*/
	public static final int DEFAULT_MAX_PER_ROUTE = 20;

	private static final String UTF_8 = "UTF-8";

	static {
		// 设置连接池  
		httpConnectionManager = new PoolingHttpClientConnectionManager();

		// 设置连接池大小  
		httpConnectionManager.setMaxTotal(MAX_TOTAL);
		httpConnectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);

		RequestConfig.Builder configBuilder = RequestConfig.custom();
		// 设置连接超时  
		configBuilder.setConnectTimeout(MAX_TIMEOUT);
		// 设置读取超时  
		configBuilder.setSocketTimeout(MAX_TIMEOUT);
		// 设置从连接池获取连接实例的超时  
		configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
		// 在提交请求之前 测试连接是否可用 (Deprecated)
		//configBuilder.setStaleConnectionCheckEnabled(true);

		//设置代理抓包
		requestConfig = configBuilder.build();
	}

	/* http 请求*/
	public static HttpResult httpGet(HttpGet httpGet) {
		HttpClient httpClient = getHttpClient();
		return executeHttp(httpGet, httpClient);
	}

	public static HttpResult httpPost(HttpPost httpPost) {
		HttpClient httpClient = getHttpClient();
		return executeHttp(httpPost, httpClient);
	}

	/* https 请求*/
	public static HttpResult httpsGet(HttpGet httpGet) {
		//ssl连接
		HttpClient httpsClient = getHttpsClient();
		return executeHttp(httpGet, httpsClient);
	}

	public static HttpResult httpsPost(HttpPost httpPost) {
		//ssl连接
		HttpClient httpsClient = getHttpsClient();
		return executeHttp(httpPost, httpsClient);
	}

	//------------------------------http post发送 json body-----------------------------------------

	public static String postJson(String url, String json) {
		return postJson(url, null, json);
	}

	public static String postJson(String url, Map<String, String> headers, String json) {
		try {
			HttpPost httpPost = new HttpPost(url);
			addHeaders(headers, httpPost);
			StringEntity postEntity = new StringEntity(json, Charset.forName(UTF_8));
			postEntity.setContentType("text/json");
			httpPost.setEntity(postEntity);
			HttpClient httpClient = getHttpClient();
			HttpResult hr = executeHttp(httpPost, httpClient);
			return hr.getResult();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 从实体类得到http-get请求参数字符串(形如:?p1=v1&p2=v2..)
	 */
	public static String getParams(final Object paramForm) {
		Mirror<?> mirror = Mirror.me(paramForm);
		Field[] fields = mirror.getFields();
		List<String> fieldNames = Lists.transform(CollectionUtil.list(fields), new Function<Field, String>() {
			@Override
			public String apply(Field f) {
				Object val = mirror.getValue(paramForm, f);
				if (Util.isEmpty(val)) {
					return null;
				}
				return f.getName() + "=" + val;
			}
		});

		StringBuffer sb = new StringBuffer("?");
		sb.append(Joiner.on("&").skipNulls().join(fieldNames));
		return sb.toString();
	}

	private static void addHeaders(Map<String, String> headers, HttpPost post) {
		if (headers != null && !headers.isEmpty()) {
			for (Entry<String, String> entry : headers.entrySet()) {
				log.debug(String.format("[%s]:[%s]", entry.getKey(), entry.getValue()));
				post.addHeader(entry.getKey(), entry.getValue());
			}
		}
	}

	private static HttpResult executeHttp(HttpRequestBase request, HttpClient httpClient) {
		HttpResult hr = new HttpResult();
		String result = "";
		try {
			HttpResponse response = httpClient.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			log.debug("statusCode:" + statusCode);

			if (200 == statusCode) {
				HttpEntity entity = response.getEntity();
				result = EntityUtils.toString(entity, UTF_8);
				// 一般来说都要删除多余的字符   
				result.replaceAll("\r", "");//去掉返回结果中的"\r"字符，否则会在结果字符串后面显示一个小方格
			} else {
				request.abort();
			}
			hr.setStatusCode(statusCode);
			hr.setResult(result);
		} catch (ConnectionPoolTimeoutException e) {
			log.error("http get throw ConnectionPoolTimeoutException(wait time out)");
		} catch (ConnectTimeoutException e) {
			log.error("http get throw ConnectTimeoutException");
		} catch (SocketTimeoutException e) {
			log.error("http get throw SocketTimeoutException");
		} catch (Exception e) {
			log.error("http get throw Exception");
			e.printStackTrace();
		} finally {
			request.abort();
		}
		return hr;
	}

	/** 
	 * 创建SSL安全连接 
	 * @return 
	 */
	private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
		SSLConnectionSocketFactory sslsf = null;
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				@Override
				public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
					return true;
				}
			}).build();

			sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					return true;
				}

				@Override
				public void verify(String host, SSLSocket ssl) throws IOException {
				}

				@Override
				public void verify(String host, X509Certificate cert) throws SSLException {
				}

				@Override
				public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
				}
			});
		} catch (GeneralSecurityException e) {
			e.printStackTrace();
		}
		return sslsf;
	}

	public static CloseableHttpClient getHttpClient() {
		CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(httpConnectionManager)
				.setDefaultRequestConfig(requestConfig).build();
		return httpClient;
	}

	public static CloseableHttpClient getHttpsClient() {
		CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
				.setConnectionManager(httpConnectionManager).setDefaultRequestConfig(requestConfig).build();
		return httpClient;
	}

}
